//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/image.h>
#include <buola/image/font/cfontface.h>
#include <buola/image/font/cscaledfont.h>

#include "charfbuzzlayoutengine.h"
#include <harfbuzz/hb-ft.h>
#include <harfbuzz/hb-icu.h>

namespace buola { namespace img {

static hb_bool_t buola_hb_get_glyph(hb_font_t *pFont,void *pData,hb_codepoint_t pUnicode,
                                    hb_codepoint_t pVariation,hb_codepoint_t *pGlyph,void*)
{
    CHarfBuzzLayoutEngine *lEngine=(CHarfBuzzLayoutEngine*)pData;

    *pGlyph=lEngine->Font()->GetGlyph(pUnicode,lEngine->SubFont());

    return *pGlyph!=0;
}

static hb_position_t buola_hb_get_glyph_h_advance(hb_font_t *pFont,void *pData,hb_codepoint_t pGlyph,void*)
{
    CHarfBuzzLayoutEngine *lEngine=(CHarfBuzzLayoutEngine*)pData;
    
    return lEngine->Font()->GetAdvance(lEngine->SubFont(),pGlyph)*64;
}

static hb_position_t buola_hb_get_glyph_v_advance(hb_font_t *pFont,void *pData,hb_codepoint_t pGlyph,void*)
{
    return 0;
}

static hb_bool_t buola_hb_get_glyph_h_origin(hb_font_t *pFont,void *pData,hb_codepoint_t pGlyph,
                                                 hb_position_t *pX,hb_position_t *pY,void*)
{
    return true;
}

static hb_bool_t buola_hb_get_glyph_v_origin(hb_font_t *pFont,void *pData,hb_codepoint_t pGlyph,
                                                 hb_position_t *pX,hb_position_t *pY,void*)
{
    return true;
}

static hb_position_t buola_hb_get_glyph_h_kerning(hb_font_t *pFont,void *pData,hb_codepoint_t pLeftGlyph,
                                                  hb_codepoint_t pRightGlyph,void*)
{
    CHarfBuzzLayoutEngine *lEngine=(CHarfBuzzLayoutEngine*)pData;

    FT_Face lFace=lEngine->Font()->GetFontFace(lEngine->SubFont())->GetFTFace();
    FT_Vector lKerning;

    if(FT_Get_Kerning(lFace,pLeftGlyph,pRightGlyph,FT_KERNING_DEFAULT,&lKerning))
        return 0;
    
    return lKerning.x;
}

static hb_bool_t buola_hb_get_glyph_extents(hb_font_t *pFont,void *pData,hb_codepoint_t pGlyph,
                                            hb_glyph_extents_t *pExtents,void*)
{
    CHarfBuzzLayoutEngine *lEngine=(CHarfBuzzLayoutEngine*)pData;

    CGlyphInfo lInfo;
    lInfo.mFont=lEngine->SubFont();
    lInfo.mGlyph=pGlyph;

    lEngine->Font()->FillGlyphInfo(lInfo);

//    pExtents->x=lInfo.mX*64;
//    pExtents->y=lInfo.mY*64;
    pExtents->width=lInfo.mW*64;
    pExtents->height=lInfo.mH*64;
    pExtents->x_bearing=lInfo.mXOff*64;
    pExtents->y_bearing=lInfo.mYOff*64;

    return true;
}

static hb_bool_t buola_hb_get_glyph_contour_point(hb_font_t *pFont,void *pData,hb_codepoint_t pGlyph,
                                                  unsigned int pPointIndex,hb_position_t *pX,hb_position_t *pY,void*)
{
    msg_warn() << "getting contour point... not implemented!!\n";
    return false;
}

static hb_font_funcs_t *get_hb_font_funcs()
{
    static hb_font_funcs_t *lFuncs=nullptr;

    if(!lFuncs)
    {
        lFuncs=hb_font_funcs_create();
        hb_font_funcs_set_glyph_func(lFuncs,buola_hb_get_glyph,nullptr,nullptr);
        hb_font_funcs_set_glyph_h_advance_func(lFuncs,buola_hb_get_glyph_h_advance,nullptr,nullptr);
        hb_font_funcs_set_glyph_v_advance_func(lFuncs,buola_hb_get_glyph_v_advance,nullptr,nullptr);
        hb_font_funcs_set_glyph_h_origin_func(lFuncs,buola_hb_get_glyph_h_origin,nullptr,nullptr);
        hb_font_funcs_set_glyph_v_origin_func(lFuncs,buola_hb_get_glyph_v_origin,nullptr,nullptr);
        hb_font_funcs_set_glyph_h_kerning_func(lFuncs,buola_hb_get_glyph_h_kerning,nullptr,nullptr);
        hb_font_funcs_set_glyph_extents_func(lFuncs,buola_hb_get_glyph_extents,nullptr,nullptr);
        hb_font_funcs_set_glyph_contour_point_func(lFuncs,buola_hb_get_glyph_contour_point,nullptr,nullptr);
    }

    return lFuncs;
}
    
CHarfBuzzLayoutEngine::CHarfBuzzLayoutEngine(PFont pFont,TFontIndex pSubFont,UScriptCode pScript)
    :   CLayoutEngine(pFont,pSubFont)
    ,   mScript(pScript)
{
    FT_Face lFace=pFont->GetFontFace(pSubFont)->GetFTFace();
    
    mHBFace=hb_ft_face_create(lFace,nullptr);
    mHBFont=hb_font_create(mHBFace);
    hb_font_set_funcs(mHBFont,get_hb_font_funcs(),this,nullptr);
    hb_font_set_scale(mHBFont,(lFace->size->metrics.x_scale*lFace->units_per_EM)>>16,
                              (lFace->size->metrics.x_scale*lFace->units_per_EM)>>16);
    hb_font_set_ppem(mHBFont,lFace->size->metrics.x_ppem,lFace->size->metrics.y_ppem);
}

static const hb_script_t sHBScripts[]=
{
    HB_SCRIPT_COMMON,
    HB_SCRIPT_INHERITED,
    HB_SCRIPT_ARABIC,
    HB_SCRIPT_ARMENIAN,
    HB_SCRIPT_BENGALI,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_CYRILLIC,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_DEVANAGARI,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_GEORGIAN,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_GREEK,
    HB_SCRIPT_GUJARATI,
    HB_SCRIPT_GURMUKHI,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_HANGUL,
    HB_SCRIPT_HEBREW,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_KANNADA,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_KHMER,
    HB_SCRIPT_LAO,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_MALAYALAM,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_MYANMAR,
    HB_SCRIPT_OGHAM,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_ORIYA,
    HB_SCRIPT_RUNIC,
    HB_SCRIPT_SINHALA,
    HB_SCRIPT_SYRIAC,
    HB_SCRIPT_TAMIL,
    HB_SCRIPT_TELUGU,
    HB_SCRIPT_THAANA,
    HB_SCRIPT_THAI,
    HB_SCRIPT_TIBETAN,

    HB_SCRIPT_COMMON,

    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,

    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,

    HB_SCRIPT_COMMON,

    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,

    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
    HB_SCRIPT_COMMON,
};

void CHarfBuzzLayoutEngine::LayoutChars(const std::u16string &pUText,int pFrom,int pTo,
        std::vector<CTextLayout::SGlyph> &pGlyphs,
        std::vector<int> &pCharToGlyphMap,bool pRTL)
{
    hb_buffer_t *lBuf=hb_buffer_create();
    hb_buffer_set_unicode_funcs(lBuf,hb_icu_get_unicode_funcs());
    hb_buffer_set_direction(lBuf,pRTL?HB_DIRECTION_RTL:HB_DIRECTION_LTR);
    hb_buffer_set_script(lBuf,sHBScripts[mScript]);
    hb_buffer_set_language(lBuf,hb_language_get_default());
   
    int lFirstGlyph=pGlyphs.size();
    //int lFirstMap=pCharToGlyphMap.size();

    int lCount=pTo-pFrom;

    pGlyphs.reserve(lFirstGlyph+lCount+100);

    hb_buffer_add_utf16(lBuf,reinterpret_cast<const uint16_t*>(pUText.data()),pUText.length(),pFrom,lCount);
    hb_shape(mHBFont,lBuf,nullptr,0);

    unsigned int lGlyphCount;
    hb_glyph_info_t *lGlyphInfo=hb_buffer_get_glyph_infos(lBuf,&lGlyphCount);
    hb_glyph_position_t *lGlyphPos=hb_buffer_get_glyph_positions(lBuf,&lGlyphCount);

    for(int i=0;i<lGlyphCount;i++)
    {
        CTextLayout::SGlyph lGlyph;
        lGlyph.mFont=mSubFont;
        lGlyph.mGlyph=lGlyphInfo[i].codepoint;
        lGlyph.mAdvance=round(lGlyphPos[i].x_advance/64.0);
        lGlyph.mOffset=CPoint_d(lGlyphPos[i].x_offset/64.0,lGlyphPos[i].y_offset/64.0);
        
        pGlyphs.push_back(lGlyph);
    }

    for(int i=0;i<lCount;i++)
    {
        pCharToGlyphMap.push_back(lFirstGlyph+i); ///\todo lClusters[i]);
    }

    //if it was RTL, reverse to return it in logical order
    if(pRTL)
    {
//        std::reverse(pGlyphs.begin()+lFirstGlyph,pGlyphs.end());
//        std::reverse(pCharToGlyphMap.begin()+lFirstMap,pCharToGlyphMap.end());
    }
}

/*namespace img*/ } /*namespace buola*/ }
